package com.backend.server.controller;

import com.backend.server.license.*;
import com.backend.server.service.LicenseCreateService;
import com.backend.server.service.LicenseInfoService;
import com.backend.server.service.UserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.common.server.common.BaseResponse;
import com.common.server.common.ErrorCode;
import com.common.server.common.ResultUtils;
import com.common.server.constant.LicenseConstant;
import com.common.server.entity.license.LicenseInfo;
import com.common.server.entity.license.dto.LicenseDownloadRequest;
import com.common.server.entity.license.dto.LicenseInfoQueryRequest;
import com.common.server.entity.license.dto.LicenseRevokeRequest;
import com.common.server.entity.license.vo.LicenseContentVO;
import com.common.server.entity.license.vo.LicenseInfoVO;
import com.common.server.entity.user.UserInfo;
import com.common.server.enums.LicenseStatusEnum;
import com.common.server.enums.UserRoleEnum;
import com.common.server.exception.ThrowUtils;
import de.schlichtherle.license.LicenseCreator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;


@RestController
@Api(value = "许可证管理", tags = "许可证管理")
@RequestMapping("/license")
@Slf4j
public class LicenseInfoController {
    @Resource
    private LicenseCreateService licenseCreateService;
    @Resource
    private LicenseInfoService licenseInfoService;
    @Resource
    private UserInfoService userInfoService;

    /**
     * 生成 License 证书
     *
     * @param param 生成证书所需参数
     * @return 生成结果
     */
    @ApiOperation(value = "License 证书生成", notes = "License 证书生成")
    @PostMapping(value = "/generateLicense")
    public BaseResponse<LicenseContentVO> generateLicense(@RequestBody LicenseCreatorParamRequest param, HttpServletRequest request) {
        // 1.校验 Controller 层参数
        ThrowUtils.throwIf(ObjectUtils.isEmpty(param), ErrorCode.PARAMS_ERROR, "生成证书所需参数不能为空");
        // 2.生成证书
        LicenseContentVO result = licenseCreateService.generateLicense(param, request);
        // 3.返回结果
        return ResultUtils.success(result, "License 证书生成成功");
    }

    /**
     * 下载证书
     *
     * @param licenseDownloadRequest 下载参数
     */
    @ApiOperation(value = "License 证书下载", notes = "License 证书下载")
    @PostMapping("/download")
    public void downloadLicense(@RequestBody
                                LicenseDownloadRequest licenseDownloadRequest, HttpServletRequest request, HttpServletResponse response) {
        // 校验参数
        ThrowUtils.throwIf(ObjectUtils.isEmpty(licenseDownloadRequest), ErrorCode.PARAMS_ERROR, "证书序号不能为空");
        String licenseId = licenseDownloadRequest.getLicenseId();
        ThrowUtils.throwIf(StringUtils.isBlank(licenseId), ErrorCode.PARAMS_ERROR, "证书序号不能为空");
        ThrowUtils.throwIf(licenseId.length() != 36, ErrorCode.PARAMS_ERROR, "证书序号长度必须为 36 位");
        QueryWrapper<LicenseInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", licenseId);
        int count = licenseInfoService.count(queryWrapper);
        ThrowUtils.throwIf(count == 0, ErrorCode.NOT_FOUND_ERROR, "指定证书不存在, 证书下载失败");

        // 1.登录用户状态
        UserInfo loginUser = userInfoService.getLoginUser(request);
        ThrowUtils.throwIf(ObjectUtils.isEmpty(loginUser), ErrorCode.NOT_LOGIN_ERROR, "用户未登录, 无法下载证书");
        // 2.管理员权限
        ThrowUtils.throwIf(!UserRoleEnum.ADMIN.getValue().equals(loginUser.getUserRole()), ErrorCode.NO_AUTH_ERROR, "非管理员, 无权限下载证书");
        // 本地文件路径
        Properties prop = new Properties();
        try {
            prop.load(LicenseCreator.class.getResourceAsStream("/application.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 证书下载路径
        String filePath = Paths.get(LicenseConstant.USER_DIR, prop.getProperty("license.license-path"), licenseId + ".lic").toString();
        Path path = Paths.get(filePath);
        // String filePath = System.getProperty("user.dir") + "/backend-server/src/main/resources/license/new.png";
        // 检查文件是否存在
        ThrowUtils.throwIf(StringUtils.isBlank(filePath) || !Files.exists(path), ErrorCode.PARAMS_ERROR, "指定证书不存在, 证书下载失败");
        // 下载时文件名（客户端保存时看到的文件名）
        String downloadFileName = licenseId + ".lic";
        // String downloadFileName = "new.png";
        try {
            // 使用缓冲输入流读取本地文件
            BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(path));
            // 使用HttpServletResponse的输出流将文件发送给客户端
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            // 设置响应头
            // response.setContentType("application/octet-stream"); // 设置为通用二进制文件类型
            response.setContentType("image/png"); // 根据文件类型设置正确的MIME类型
            response.setHeader("Content-Disposition", "attachment; filename=\"" + downloadFileName + "\"");
            // 设置Content-Disposition头部以触发下载
            response.setContentLength((int) new File(filePath).length()); // 可选：设置内容长度，有助于浏览器正确处理下载
            // 读取文件内容并写入响应输出流
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            // 关闭流
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("文件下载失败", e);
        }
    }

    /**
     * 撤销证书
     *
     * @param licenseRevokeRequest 撤销参数
     */
    @ApiOperation(value = "License 证书撤销", notes = "License 证书撤销")
    @PostMapping("/revoke")
    public BaseResponse<Boolean> downloadLicense(@RequestBody
                                                 LicenseRevokeRequest licenseRevokeRequest, HttpServletRequest request) {
        // 1.校验 Controller 层参数
        // 证书状态
        ThrowUtils.throwIf(ObjectUtils.isEmpty(licenseRevokeRequest), ErrorCode.PARAMS_ERROR, "证书序号不能为空");
        String licenseId = licenseRevokeRequest.getLicenseId();
        ThrowUtils.throwIf(StringUtils.isBlank(licenseId), ErrorCode.PARAMS_ERROR, "证书序号不能为空");
        ThrowUtils.throwIf(licenseId.length() != 36, ErrorCode.PARAMS_ERROR, "证书序号长度必须为 36 位");
        QueryWrapper<LicenseInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", licenseId);
        int count = licenseInfoService.count(queryWrapper);
        ThrowUtils.throwIf(count == 0, ErrorCode.NOT_FOUND_ERROR, "指定证书不存在");
        // 登录用户状态
        UserInfo loginUser = userInfoService.getLoginUser(request);
        ThrowUtils.throwIf(ObjectUtils.isEmpty(loginUser), ErrorCode.NOT_LOGIN_ERROR, "用户未登录, 无法下载证书");
        // 管理员权限
        ThrowUtils.throwIf(!UserRoleEnum.ADMIN.getValue().equals(loginUser.getUserRole()), ErrorCode.NO_AUTH_ERROR, "非管理员, 无权限下载证书");
        // 2.撤销证书
        // 更改证书状态
        LicenseInfo licenseInfo = new LicenseInfo();
        licenseInfo.setId(licenseId);
        licenseInfo.setStatus(LicenseStatusEnum.REVOKED.getValue());
        boolean updateById = licenseInfoService.updateById(licenseInfo);
        ThrowUtils.throwIf(!updateById, ErrorCode.OPERATION_ERROR, "撤销证书失败, 请稍后重试");
        licenseInfo.setStatus(LicenseStatusEnum.REVOKED.getValue());
        // 数据库删除
        boolean removeById = licenseInfoService.removeById(licenseId);
        ThrowUtils.throwIf(!removeById, ErrorCode.OPERATION_ERROR, "撤销证书失败, 请稍后重试");
        // 文件目录删除(不需要, 证书可以保留)
        // 3.返回结果
        return ResultUtils.success(removeById, "License 证书撤销成功");
    }

    /**
     * 获取 License 证书列表
     *
     * @param request 查询参数
     */
    @ApiOperation(value = "获取 License 证书列表", notes = "获取 License 证书列表")
    @PostMapping(value = "/getLicenseInfoList")
    public BaseResponse<Page<LicenseInfoVO>> getLicenseInfoList(@RequestBody LicenseInfoQueryRequest request) {
        // 1.校验 Controller 层参数
        ThrowUtils.throwIf(ObjectUtils.isEmpty(request), ErrorCode.PARAMS_ERROR, "获取证书列表所需参数不能为空");
        // 2.获取证书列表
        Page<LicenseInfoVO> licenseInfoVOPage = licenseInfoService.getLicenseInfoPage(request);
        // 3.返回结果
        return ResultUtils.success(licenseInfoVOPage, "获取证书列表成功");
    }

    /**
     * 获取服务器硬件信息
     *
     * @param osName 操作系统类型，如果为空则自动判断
     * @return com.ccx.models.license.LicenseCheckModel
     */
    @ApiIgnore
    @ApiOperation(value = "获取服务器硬件信息", notes = "获取服务器硬件信息")
    @GetMapping(value = "/getServerInfos")
    public LicenseCheckModel getServerInfos(@RequestParam(value = "osName", required = false) String osName) {
        // 操作系统类型
        if (StringUtils.isBlank(osName)) {
            osName = System.getProperty("os.name");
        }
        osName = osName.toLowerCase();
        AbstractServerInfos abstractServerInfos = null;
        // 根据不同操作系统类型选择不同的数据获取方法
        if (osName.startsWith("windows")) {
            abstractServerInfos = new WindowsServerInfos();
        } else if (osName.startsWith("linux")) {
            abstractServerInfos = new LinuxServerInfos();
        } else {// 其他服务器类型
            abstractServerInfos = new LinuxServerInfos();
        }
        // 返回硬件信息
        return abstractServerInfos.getServerInfos();
    }

    /**
     * 测试文件下载
     *
     * @param response
     * @param licenseId
     */
    @ApiIgnore
    @PostMapping("/test")
    public void test(HttpServletResponse response, String licenseId) {
        // 本地文件路径
        Properties prop = new Properties();
        try {
            prop.load(LicenseCreator.class.getResourceAsStream("/application.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 证书下载路径
        String filePath = Paths.get(LicenseConstant.USER_DIR, prop.getProperty("license.license-path"), licenseId + ".lic").toString();
        // String filePath = System.getProperty("user.dir") + "/backend-server/src/main/resources/license/new.png";
        // 下载时文件名（客户端保存时看到的文件名）
        String downloadFileName = "license.lic";
        // String downloadFileName = "new.png";
        try {
            // 使用缓冲输入流读取本地文件
            BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)));
            // 使用HttpServletResponse的输出流将文件发送给客户端
            // ServletOutputStream outputStream = response.getOutputStream();
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            // 设置响应头
            response.setContentType("application/octet-stream"); // 设置为通用二进制文件类型
            // response.setContentType("application/x-png"); // 根据文件类型设置正确的MIME类型
            // response.setContentType("image/png"); // 根据文件类型设置正确的MIME类型
            response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
            // response.addHeader("Pargam", "no-cache");
            // response.addHeader("Cache-Control", "no-cache");
            // 设置Content-Disposition头部以触发下载
            // response.setContentLength((int) new File(filePath).length()); // 可选：设置内容长度，有助于浏览器正确处理下载
            // 读取文件内容并写入响应输出流
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            // 关闭流
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("文件下载失败", e);
        }
    }
}
